﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Threading;

namespace SmartMathLibrary.Management.Monitoring
{
    /// <summary>
    /// Occurs when the message limit of the monitor get exceed.
    /// </summary>
    public delegate void ProcessorUsageLimitExceedanceEventHandler(double processorUsage);

    /// <summary>
    /// The PerformanceMonitor provides methods to monitor the processor usage.
    /// </summary>
    [Serializable]
    public class PerformanceMonitor : IMonitor, IDisposable
    {
        /// <summary>
        /// The message limit.
        /// </summary>
        private double messageLimit;

        /// <summary>
        /// The sampling rate
        /// </summary>
        private double samplingRate;

        /// <summary>
        /// The timer for checking processor usage.
        /// </summary>
        private readonly DispatcherTimer samplingTimer;

        /// <summary>
        /// Defines the priority of the monitor.
        /// </summary>
        private readonly MonitoringPriority monitoringPriority;

        /// <summary>
        /// The performance counter, which get the processor usage.
        /// </summary>
        private readonly PerformanceCounter performanceCounter;

        /// <summary>
        /// Occurs when the message limit get exceed.
        /// </summary>
        public event ProcessorUsageLimitExceedanceEventHandler ProcessorUsageEvent;

        /// <summary>
        /// Initializes a new instance of the <see cref="PerformanceMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a LimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        public PerformanceMonitor(double messageLimit, double samplingRate)
        {
            this.samplingRate = samplingRate;
            this.messageLimit = messageLimit;
            this.monitoringPriority = MonitoringPriority.Normal;
            this.samplingTimer = new DispatcherTimer(DispatcherPriority.Normal);
            this.performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            this.samplingTimer.Interval = TimeSpan.FromMilliseconds(samplingRate);
            this.samplingTimer.Tick += new EventHandler(samplingTimer_Tick);
            this.samplingTimer.Start();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PerformanceMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a ProcessorUsageLimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="startMonitoring">if set to <c>true</c> the monitor will start automatically
        /// otherwise, it must be started by method Start().</param>
        public PerformanceMonitor(double messageLimit, double samplingRate, bool startMonitoring)
        {
            this.samplingRate = samplingRate;
            this.messageLimit = messageLimit;
            this.monitoringPriority = MonitoringPriority.Normal;
            this.samplingTimer = new DispatcherTimer(DispatcherPriority.Normal);
            this.performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            this.samplingTimer.Interval = TimeSpan.FromMilliseconds(samplingRate);
            this.samplingTimer.Tick += new EventHandler(samplingTimer_Tick);

            if (startMonitoring)
            {
                this.samplingTimer.Start();
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PerformanceMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a ProcessorUsageLimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="monitoringPriority">The monitoring priority.</param>
        public PerformanceMonitor(double messageLimit, double samplingRate, MonitoringPriority monitoringPriority)
        {
            DispatcherPriority tempuri = DispatcherPriority.Normal;

            switch (monitoringPriority)
            {
                case MonitoringPriority.ApplicationIdle:

                    tempuri = DispatcherPriority.ApplicationIdle;
                    break;

                case MonitoringPriority.Background:

                    tempuri = DispatcherPriority.Background;
                    break;

                case MonitoringPriority.SystemIdle:

                    tempuri = DispatcherPriority.SystemIdle;
                    break;

                case MonitoringPriority.Normal:

                    tempuri = DispatcherPriority.Normal;
                    break;
            }

            this.samplingRate = samplingRate;
            this.messageLimit = messageLimit;
            this.monitoringPriority = monitoringPriority;
            this.samplingTimer = new DispatcherTimer(tempuri);
            this.performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            this.samplingTimer.Interval = TimeSpan.FromMilliseconds(samplingRate);
            this.samplingTimer.Tick += new EventHandler(samplingTimer_Tick);
            this.samplingTimer.Start();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PerformanceMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a ProcessorUsageLimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="monitoringPriority">The monitoring priority.</param>
        /// <param name="startMonitoring">if set to <c>true</c> the monitor will start automatically
        /// otherwise, it must be started by method Start().</param>
        public PerformanceMonitor(double messageLimit, double samplingRate, MonitoringPriority monitoringPriority,
                                  bool startMonitoring)
        {
            DispatcherPriority tempuri = DispatcherPriority.Normal;

            switch (monitoringPriority)
            {
                case MonitoringPriority.ApplicationIdle:

                    tempuri = DispatcherPriority.ApplicationIdle;
                    break;

                case MonitoringPriority.Background:

                    tempuri = DispatcherPriority.Background;
                    break;

                case MonitoringPriority.SystemIdle:

                    tempuri = DispatcherPriority.SystemIdle;
                    break;

                case MonitoringPriority.Normal:

                    tempuri = DispatcherPriority.Normal;
                    break;
            }

            this.samplingRate = samplingRate;
            this.messageLimit = messageLimit;
            this.monitoringPriority = monitoringPriority;
            this.samplingTimer = new DispatcherTimer(tempuri);
            this.performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");

            this.samplingTimer.Interval = TimeSpan.FromMilliseconds(samplingRate);
            this.samplingTimer.Tick += new EventHandler(samplingTimer_Tick);

            if (startMonitoring)
            {
                this.samplingTimer.Start();
            }
        }

        /// <summary>
        /// Gets the monitoring priority.
        /// </summary>
        /// <value>The monitoring priority.</value>
        public MonitoringPriority MonitoringPriority
        {
            get { return monitoringPriority; }
        }

        /// <summary>
        /// Gets or sets the message limit.
        /// </summary>
        /// <value>The message limit.</value>
        public double MessageLimit
        {
            get { return messageLimit; }
            set { messageLimit = value; }
        }

        /// <summary>
        /// Gets the current processor usage.
        /// </summary>
        /// <value>The current processor usage.</value>
        public double ProcessorUsage
        {
            get { return this.performanceCounter.NextValue(); }
        }

        /// <summary>
        /// Gets or sets the sampling rate of the monitor.
        /// </summary>
        /// <value>The sampling rate of the monitor.</value>
        public double SamplingRate
        {
            get { return samplingRate; }
            set { samplingRate = value; }
        }

        /// <summary>
        /// Starts the monitoring.
        /// </summary>
        public void StartMonitoring()
        {
            this.samplingTimer.Start();
        }

        /// <summary>
        /// Stops the monitoring.
        /// </summary>
        public void StopMonitoring()
        {
            this.samplingTimer.Stop();
        }

        /// <summary>
        /// Handles the Tick event of the samplingTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void samplingTimer_Tick(object sender, EventArgs e)
        {
            double usage = this.ProcessorUsage;

            if (usage >= this.messageLimit)
            {
                this.ProcessorUsageEvent(usage);
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
            this.Dispose(true);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        public virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.performanceCounter.Dispose();
                GC.SuppressFinalize(this);
            }
        }
    }
}